package it.koutifaris.travianmap.ui.viewstate;

import it.koutifaris.travianmap.ui.MapDetailsLevel;
import android.os.Parcel;
import android.os.Parcelable;

public class ViewStateImpl implements DynamicViewState {

	private static final float MIN_ZOOM = 0.2f;
	private static final float MAX_ZOOM = 2;

	private int x;
	private int y;
	private float zoom;
	private final int baseSquareSize;
	private ViewStateListener listener;
	private XYSquare selectedSquare;

	ViewStateImpl(int x, int y, float zoom, int squareSize, ViewStateListener listener) {
		this(x, y, zoom, squareSize, listener, null);
	}

	ViewStateImpl(int x, int y, float zoom, int squareSize, ViewStateListener listener, XYSquare selectedSquare) {
		super();
		this.x = x;
		this.y = y;
		this.zoom = getInRangeZoom(zoom);
		this.baseSquareSize = squareSize;
		this.listener = listener;
	}

	@Override
	public void updatePosition(int x, int y) {
		this.x = x;
		this.y = y;
		listener.onViewStateChanged();
	}

	@Override
	public int getX() {
		return x;
	}

	@Override
	public int getY() {
		return y;
	}

	@Override
	public float getZoom() {
		return zoom;
	}

	@Override
	public void setZoom(float zoom) {
		setZoom(zoom, 0, 0);
	}

	@Override
	public void setZoom(float zoom, float centerX, float centerY) {
		float lastZoom = this.zoom;
		int lastSquareSize = this.getSquareSize();
		this.zoom = getInRangeZoom(zoom);

		if (lastZoom != this.zoom) {
			int cx = Math.round(centerX);
			int cy = Math.round(centerY);

			x = calculateNewCoord(x + cx, lastZoom, lastSquareSize) - cx;
			y = calculateNewCoord(y + cy, lastZoom, lastSquareSize) - cy;

			listener.onViewStateChanged();
		}
	}

	private float getInRangeZoom(float zoom) {
		return Math.min(Math.max(zoom, MIN_ZOOM), MAX_ZOOM);
	}

	private int calculateNewCoord(int point, float lastZoom, int lastSquareSize) {
		int squareNum = point / lastSquareSize;
		int squarePoint = squareNum * lastSquareSize;
		int distanceFromSquare = point - squarePoint;
		int translation = (int) (distanceFromSquare * (this.zoom / lastZoom));
		int newSquarePoint = squareNum * this.getSquareSize();

		return newSquarePoint + translation;
	}

	@Override
	public int getSquareSize() {
		return (int) (baseSquareSize * zoom);
	}

	@Override
	public void setListener(ViewStateListener listener) {
		if (listener == null) {
			listener = NullViewStateListener.nullViewStateListener();
		}

		this.listener = listener;
	}

	@Override
	public MapDetailsLevel getDetailsLevel() {
		if (getSquareSize() < 20) {
			return MapDetailsLevel.LOW;
		}

		return MapDetailsLevel.HIGH;
	}

	@Override
	public void setSelectedSquare(int x, int y) {
		this.selectedSquare = new XYSquareImpl(x, y);
		listener.onViewStateChanged();
	}

	@Override
	public void clearSelectedSquare() {
		this.selectedSquare = null;
		listener.onViewStateChanged();
	}

	@Override
	public XYSquare getSelectedSquare() {
		return selectedSquare;
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(x);
		dest.writeInt(y);
		dest.writeFloat(zoom);
		dest.writeInt(baseSquareSize);
		dest.writeParcelable(selectedSquare, flags);
	}

	public final static Parcelable.Creator<ViewStateImpl> CREATOR = new Parcelable.Creator<ViewStateImpl>() {

		@Override
		public ViewStateImpl createFromParcel(Parcel source) {
			int x = source.readInt();
			int y = source.readInt();
			float zoom = source.readFloat();
			int squareSize = source.readInt();
			XYSquareImpl selectedSquare = source.readParcelable(XYSquareImpl.class.getClassLoader());

			return new ViewStateImpl(x, y, zoom, squareSize, NullViewStateListener.nullViewStateListener(), selectedSquare);
		}

		@Override
		public ViewStateImpl[] newArray(int size) {
			return new ViewStateImpl[size];
		}
	};
}
